package com.onpositive.data.diskstorage;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.IPropertyMetadata;
import com.onpositive.data.IReadableObjectCollection;
import com.onpositive.data.ISeekableAccess;
import com.onpositive.data.serializer.readers.DefaultReaderFactory;
import com.onpositive.data.serializer.writers.AbstractWriter;
import com.onpositive.data.serializer.writers.IWriterFactory;
import com.onpositive.data.serializer.writers.ObjectCollectionWriter;

public class StoredCollection extends AbstractObjectCollection implements
		IReadableObjectCollection<Integer> {

	private ISeekableAccess file;
	private int collectionStart;
	private int[] dataOffsets;

	public StoredCollection(ISeekableAccess file, int start, int terminate,
			int dataSize) {
		super(dataSize);
		this.file = file;
		this.collectionStart = start;
	}

	public void setDataOffsets(int[] dataOffsets) {
		this.dataOffsets = dataOffsets;
	}

	public ArrayList<IPropertyMetadata> getMetadata(String field, String value) {

		ArrayList<IPropertyMetadata> result = new ArrayList<IPropertyMetadata>();

		for (IPropertyMetadata mtd : this.metadataArray) {

			if (mtd == null)
				continue;

			Object thisValue = mtd.getMetaPropertyValue(field);
			if (thisValue == null)
				continue;

			if (thisValue.equals(value))
				result.add(mtd);
		}

		return result;
	}

	protected AbstractReader<?> initReader(IPropertyMetadata meta) {
		Integer mtdInd = this.metadataIndexMap.get(meta);
		if (mtdInd == null)
			return null;
		if (mtdInd==-1){
			AbstractReader<?> result = DefaultReaderFactory.getInstance()
					.createReader(Integer.MIN_VALUE, meta);
			result.initEmpty(dataSize);
			recordReader(meta, result);
			return result;
		}
		int propertyStart = dataOffsets[mtdInd];
		int propertyEnd = dataOffsets[mtdInd + 1];

		int type = 0;
		try {
			file.seek(this.collectionStart + propertyStart);
			type = file.readInt();
		} catch (IOException e) {
			e.printStackTrace();
		}

		AbstractReader<?> result = DefaultReaderFactory.getInstance()
				.createReader(type, meta);
		result.setCollectionStart(this.collectionStart);
		result.setMyStart(propertyStart);
		result.setMyEnd(propertyEnd);
		result.setFile(this.file);
		recordReader(meta, result);

		return result;
	}

	public long getBitmapInitTotalTime() {

		long result = 0;
		for (AbstractReader<?> rdr : readerMap.values())
			result += rdr.getBitmapInitTime();

		return result;
	}

	public long getValuesArrayInitTotalTime() {

		long result = 0;
		for (AbstractReader<?> rdr : readerMap.values())
			result += rdr.getValuesArrayInitTime();

		return result;
	}

	public byte[] getData(IPropertyMetadata meta) {
		Integer mtdInd = this.metadataIndexMap.get(meta);
		if (mtdInd == null)
			return null;

		int propertyStart = dataOffsets[mtdInd];
		int propertyEnd = dataOffsets[mtdInd + 1];
		try {
			byte[] offsetsByteArray = new byte[propertyEnd - propertyStart];
			file.seek(collectionStart + propertyStart);
			file.readFully(offsetsByteArray);
			return offsetsByteArray;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void write(DataOutputStream stream) throws IOException {
		ObjectCollectionWriter c=new ObjectCollectionWriter();
		c.setFactory(new IWriterFactory() {
			
			@Override
			public AbstractWriter<?> createWriter(IPropertyMetadata meta) {
				AbstractReader<?> reader = readerMap.get(meta);

				if (reader == null)
					reader = initReader(meta);
				return reader.getWriter();
			}
		});
		c.append(this);
		c.serialize(stream);
	}

	@Override
	public void updateMeta(IPropertyMetadata m) {
		throw new UnsupportedOperationException();
	}

}
